const PENDING = "pending";// 等待
const FULFILLED = "fulfilled";// 成功
const REJECTED = "rejected";// 失败

/**
 * 练习题4,promise原码
 */
class MyPromise {
    /**
     * promise状态
     * @type {string}
     */
    status = PENDING;

    /**
     * 成功状态的返回值
     * @type {any}
     */
    value = undefined;

    /**
     * 失败理由
     * @type {any}
     */
    reason = undefined;

    successCallback = [];

    failCallback = [];

    constructor(executor) {
        if (!executor || !executor instanceof Function) {
            throw new TypeError("exector must be a function");
        }

        // 执行器执行,且传入,成功,失败,回调
        try {
            executor(this.resolve, this.reject);
        } catch (e) {
            this.reject(e);
        }
    }

    resolve = (value) => {
        // 状态不是等待不执行
        if (this.status !== PENDING) return;

        // 状态改为成功
        this.status = FULFILLED;
        this.value = value;

        while (this.successCallback.length) {// 调用注册的成功函数
            this.successCallback.shift()();
        }
    }

    reject = (reason) => {
        // 状态不是等待不执行
        if (this.status !== PENDING) return;

        // 状态改为失败
        this.status = REJECTED;
        this.reason = reason;

        while (this.failCallback.length) {// 调用注册的所有失败
            this.failCallback.shift()();
        }
    }

    /**
     * 回调方法注册
     * @param successCalback
     * @param failCallback
     */
    then(successCalback, failCallback) {
        // 无回调函数时,为保调用链不断,此处添加默认回调
        successCalback = successCalback ? successCalback : v => v;
        failCallback = failCallback ? failCallback : r => {
            throw r
        };

        const nextPromise = new MyPromise((resolve, reject) => {
            // 已执行完成,直接执行回调
            if (this.status === FULFILLED) {
                setTimeout(() => {
                        try {
                            const successResult = successCalback(this.value);
                            this.resolvePromise(nextPromise, successResult, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    }
                    , 0);
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                        try {
                            const failResult = failCallback(this.reason);
                            this.resolvePromise(nextPromise, failResult, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    }
                    , 0);
            } else {//未执行完回成,将回调函数存储起来
                successCalback && this.successCallback.push(() => {
                    setTimeout(() => {
                            try {
                                const successResult = successCalback(this.value);
                                this.resolvePromise(nextPromise, successResult, resolve, reject);
                            } catch (e) {
                                reject(e)
                            }
                        }
                        , 0);
                });
                failCallback && this.failCallback.push(() => {
                    setTimeout(() => {
                            try {
                                const failResult = failCallback(this.reason);
                                this.resolvePromise(nextPromise, failResult, resolve, reject);
                            } catch (e) {
                                reject(e)
                            }
                        }
                        , 0);
                });
            }
        });
        return nextPromise;
    }

    /**
     * 处理回调函数返回
     * @param result
     * @param resolve
     * @param reject
     */
    resolvePromise(nextPromise, result, resolve, reject) {
        if (nextPromise === result) {// 不能返回自已这个promise实例
            return reject(new TypeError("Chaining cycle detected for promise #<Promise>"));
        }

        if (result instanceof MyPromise) {
            result.then(resolve, reject);
        } else {
            resolve(result);
        }
    }

    /**
     * 并发调用
     */
    static all(array = []) {

        if (!array instanceof Array) {
            throw new TypeError("array must be array")
        }

        const result = [];
        let index = 0;

        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                result[key] = value;
                if (++index === array.length) {
                    resolve(result);
                }
            }

            for (let i = 0; i < array.length; i++) {
                const current = array[i];
                if (current instanceof MyPromise) {//  传入的是 Mypromise对象
                    current.then(v => addData(i, v), r => reject(r))
                } else {
                    addData(i, array[i]);
                }
            }
        })
    }

    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        }

        return new MyPromise(resolve => resolve(value));
    }

    finally(callback = v => v) {
        return this.then((value) =>
                MyPromise.resolve(callback(value)).then(() => value),
            (reason) =>
                MyPromise.resolve(callback(reason)).then(() => {
                    throw(reason)
                }),
        );
    }

    catch(failCallback) {
        return this.then(undefined, failCallback);
    }

    static reject(reason) {
        return new MyPromise((resolve, reject) => reject(reason));
    }

    /**
     * 取最先成功的那个
     * @param array
     * @returns {MyPromise}
     */
    static race(array) {
        if (!array instanceof Array) {
            throw new TypeError("array must be array")
        }

        const result = [];
        let index = 0;

        return new MyPromise((resolve, reject) => {
            function addReasons(key, reason) {// 全部失败.视为失败
                result[key] = reason;
                if (++index === array.length) {
                    reject(result);
                }
            }

            for (let i = 0; i < array.length; i++) {
                const current = array[i];
                if (current instanceof MyPromise) {// 一个成功视为成功
                    current.then(v => resolve(i, v), r => addReasons(i, r))
                } else {
                    return resolve(array[i]);
                }
            }
        })
    }
}

module.exports = MyPromise;